// socket.js
const EventTypes = ['open', 'close', 'message', 'error', 'reconnect']
const DEFAULT_CHECK_TIME = 60 * 1000 // 心跳检测的默认时间
const DEFAULT_CHECK_COUNT = 5 // 心跳检测默认失败重连次数
const DEFAULT_RECONNET_COUNT = 30 * 1000
const CLOSE_ABNORMAL = 1006 // WebSocket非正常关闭code码

class EventMap {
  deps = new Map()
  depend(eventType, callback) {
    this.deps.set(eventType, callback)
  }
  notify(eventType, event) {
    if (this.deps.has(eventType)) {
      this.deps.get(eventType)(event)
    }
  }
}

class Socket extends WebSocket {
  heartCheckInterval = null
  constructor(options, dep, reconnectCount = 0) {
    const { baseURL = null, url, query = {} } = options

    const { WS_BASE_URL } = getApp().globalData
    const _baseURL = baseURL ? baseURL : WS_BASE_URL

    const _queryParams = Object.keys(query).reduce((str, key) => {
      if (typeof query[key] !== 'object' && typeof query[key] !== 'function') {
        return (str += str.length > 0 ? `&${key}=${query[key]}` : `${key}=${query[key]}`)
      } else {
        return str
      }
    }, '')

    super(`${_baseURL}${url}?${_queryParams}`)
    this._currentOptions = options
    this._dep = dep
    this._reconnectCount = reconnectCount
    this.initSocket()
  }

  // 初始化WebSocket
  initSocket() {
    // 监听webSocket的事件
    this.onopen = function (e) {
      this._dep.notify('open', e)
      this.heartCheckStart()
    }
    this.onclose = function (e) {
      this._dep.notify('close', e)
      // 如果WebSocket是非正常关闭 则进行重连
      if (e.code === CLOSE_ABNORMAL) {
        if (this._reconnectCount < DEFAULT_CHECK_COUNT) {
          this._reconnectCount++
          setTimeout(() => {
            console.log(`第${this._reconnectCount}次重连...`)
            const _socket = new Socket(this._currentOptions, this._dep, this._reconnectCount)
            this._dep.notify('reconnect', _socket)
          }, this._reconnectCount * DEFAULT_RECONNET_COUNT)
        } else {
          return uni.showToast({
            title: 'WebSocket重连失败, 请联系平台!',
            icon: 'none',
            duration: 3000,
          })
        }
      }
    }
    this.onerror = function (e) {
      this._dep.notify('error', e)
    }
    this.onmessage = function (e) {
      // 如果后端返回的是二进制数据
      if (e.data instanceof Blob) {
        const reader = new FileReader()
        reader.readAsArrayBuffer(e.data)
        reader.onload = ev => {
          if (ev.target.readyState === FileReader.DONE) {
            this._dep.notify('message', ev.target?.result)
          }
        }
      } else {
        // 处理普通数据
        try {
          const _parseData = JSON.parse(e.data)
          this._dep.notify('message', _parseData)
        } catch (error) {
          console.log(error)
          this._dep.notify('message', e)
        }
      }
    }
  }

  // 订阅事件
  subscribe(eventType, callback) {
    if (typeof callback !== 'function') throw new Error('The second param is must be a function')
    if (!EventTypes.includes(eventType)) throw new Error('The first param is not supported')
    this._dep.depend(eventType, callback)
  }

  // 发送消息
  sendMessage(data) {
    const isObject = typeof data === 'object'
    this.send(isObject ? JSON.stringify(data) : data)
  }

  // 关闭WebSocket
  closeSocket(code, reason) {
    this.close(code, reason)
  }

  // 开始心跳检测
  heartCheckStart() {
    this.heartCheckInterval = setInterval(() => {
      if (this.readyState === this.OPEN) {
        this.sendMessage({ alive: '检测心跳' })
      } else {
        this.clearHeartCheck()
      }
    }, DEFAULT_CHECK_TIME)
  }

  // 清除心跳检测
  clearHeartCheck() {
    clearInterval(this.heartCheckInterval)
  }

  // 重置心跳检测
  resetHeartCheck() {
    clearInterval(this.heartCheckInterval)
    this.heartCheckStart()
  }
}

export default options => {
  if (!window.WebSocket)
    return uni.showToast({
      title: '您的浏览器不支持WebSocket, 请更换浏览器!',
      icon: 'none',
      duration: 3000,
    })
  const dep = new EventMap()
  return new Socket(options, dep)
}
